home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
090
/
cpp.arc
/
CPP4.C
< prev
next >
Wrap
C/C++ Source or Header
|
1985-11-27
|
27KB
|
792 lines
/*
* C P P 4 . C
* M a c r o D e f i n i t i o n s
*
* Edit History
* 31-Aug-84 MM USENET net.sources release
* 04-Oct-84 MM __LINE__ and __FILE__ must call ungetstring()
* so they work correctly with token concatenation.
* Added string formal recognition.
* 25-Oct-84 MM "Short-circuit" evaluate #if's so that we
* don't print unnecessary error messages for
* #if !defined(FOO) && FOO != 0 && 10 / FOO ...
* 31-Oct-84 ado/MM Added token concatenation
* 6-Nov-84 MM Split off eval stuff
* 1-Apr-85 ado Fixed bug in STRING_FORMAL version
* 2-May-85 MM Changed the way macro parameters work -- only
* one byte is reserved, 255 param's possible.
*/
#include <stdio.h>
#include <ctype.h>
#include "cppdef.h"
#include "cpp.h"
#if OK_CONCAT == CON_NOEXPAND
#define SEP COM_SEP /* concat token, don't reexpand */
#endif
#if OK_CONCAT == CON_EXPAND
#define SEP TOK_SEP /* concat token and reexpand */
#endif
/*
* parm[], parmp, and parlist[] are used to store #define() argument
* lists. nargs contains the actual number of parameters stored.
*/
static char parm[NPARMWORK + 1]; /* define param work buffer */
static char *parmp; /* Free space in parm */
static char *parlist[NMACPARS]; /* -> start of each parameter */
static int nargs; /* Parameters for this macro */
dodefine()
/*
* Called from control when a #define is scanned. This module
* parses formal parameters and the replacement string. When
* the formal parameter name is encountered in the replacement
* string, it is replaced by a character in the range 128 to
* 128+NPARAM (this allows up to 32 parameters within the
* Dec Multinational range). If cpp is ported to an EBCDIC
* machine, you will have to make other arrangements.
*
* There is some special case code to distinguish
* #define foo bar
* from #define foo() bar
*
* Also, we make sure that
* #define foo foo
* expands to "foo" but doesn't put cpp into an infinite loop.
*
* A warning message is printed if you redefine a symbol to a
* different text. I.e,
* #define foo 123
* #define foo 123
* is ok, but
* #define foo 123
* #define foo +123
* is not.
*
* The following subroutines are called from define():
* checkparm called when a token is scanned. It checks through the
* array of formal parameters. If a match is found, the
* token is replaced by a control byte which will be used
* to locate the parameter when the macro is expanded.
* textput puts a string in the macro work area (parm[]), updating
* parmp to point to the first free byte in parm[].
* textput() tests for work buffer overflow.
* charput puts a single character in the macro work area (parm[])
* in a manner analogous to textput().
*/
{
register int c;
register DEFBUF *dp; /* -> new definition */
int isredefine; /* TRUE if redefined */
char *old; /* Remember redefined */
extern int save(); /* Save char in work[] */
if (type[(c = skipws())] != LET)
goto bad_define;
isredefine = FALSE; /* Set if redefining */
if ((dp = lookid(c)) == NULL) /* If not known now */
dp = defendel(token, FALSE); /* Save the name */
else { /* It's known: */
isredefine = TRUE; /* Remember this fact */
old = dp->repl; /* Remember replacement */
dp->repl = NULL; /* No replacement now */
}
parlist[0] = parmp = parm; /* Setup parm buffer */
if ((c = get()) == '(') { /* With arguments? */
nargs = 0; /* Init formals counter */
do { /* Collect formal parms */
if (nargs >= NMACPARS)
cfatal("Too many arguments for macro", NULLST);
else if ((c = skipws()) == ')')
break; /* Got them all */
else if (type[c] != LET) /* Bad formal syntax */
goto bad_define;
scanid(c); /* Get the formal param */
parlist[nargs++] = parmp; /* Save its start */
textput(token); /* Save text in parm[] */
} while ((c = skipws()) == ','); /* Get another argument */
if (c != ')') /* Must end at ) */
goto bad_define;
c = ' '; /* Will skip to body */
}
else {
/*
* DEF_NOARGS is needed to distinguish between
* "#define foo" and "#define foo()".
*/
nargs = DEF_NOARGS; /* No () parameters */
}
if (type[c] == SPA) /* At whitespace? */
c = skipws(); /* Not any more. */
workp = work; /* Replacement put here */
inmacro = TRUE; /* Keep \<newline> now */
for (; c != EOF_CHAR && c != '\n'; c = get()) {
#if OK_CONCAT != CON_FALSE
if (c == '#') { /* String/concat? */
if ((c = get()) == '#') { /* Concatenate tokens? */
while (workp > work && type[workp[-1]] == SPA)
--workp; /* Erase leading spaces */
save(SEP);
c = skipws(); /* Eat whitespace */
switch (type[c]) { /* What flavor of token */
case LET:
checkparm(c, dp); /* Save it normally */
break;
case DIG:
do { /* Stuff the digits */
save(c);
c = get();
} while (type[c] == DIG);
break;
default:
ciwarn("Strange character after # (%d.)", c);
save(c);
break;
}
save(SEP); /* Delimit 2nd token */
}
else { /* Stringize */
unget(); /* Gotta rescan it */
/*
* We store a magic cookie (which becomes " on output)
* so the macro expander doesn't block expansion
* of the actual parameter. For example,
* #define abc(a) #a
* abc(__LINE__)
* should yield "123", not "__LINE__".
* This is a hack, and probably going to cause trouble.
*/
save(ST_QUOTE);
if ((c = isformal(skipws())) == 0) {
cwarn("Expected formal parameter, got \"%s\"", token);
mtokensave(dp);
}
save(ST_QUOTE);
}
continue; /* Done with this token */
}
#endif
switch (type[c]) {
case LET:
checkparm(c, dp); /* Might be a formal */
break;
case DIG: /* Number in mac. body */
case DOT: /* Maybe a float number */
scannumber(c, save); /* Scan it off */
break;
case QUO: /* String in mac. body */
#if STRING_FORMAL
stparmscan(c, dp); /* Do string magic */
#else
stparmscan(c);
#endif
break;
case BSH: /* Backslash */
save('\\');
if ((c = get()) == '\n')
wrongline = TRUE;
save(c);
break;
case SPA: /* Absorb whitespace */
/*
* Note: the "end of comment" marker is passed on
* to allow comments to separate tokens.
*/
if (workp[-1] == ' ') /* Absorb multiple */
break; /* spaces */
else if (c == '\t')
c = ' '; /* Normalize tabs */
/* Fall through to store character */
default: /* Other character */
save(c);
break;
}
}
inmacro = FALSE; /* Stop newline hack */
unget(); /* For control check */
if (workp > work && workp[-1] == ' ') /* Drop trailing blank */
workp--;
*workp = EOS; /* Terminate work */
dp->repl = savestring(work); /* Save the string */
dp->nargs = nargs; /* Save arg count */
#if DEBUG
if (debug)
dumpadef("macro definition", dp);
#endif
if (isredefine) { /* Error if redefined */
if ((old != NULL && dp->repl != NULL && !streq(old, dp->repl))
|| (old == NULL && dp->repl != NULL)
|| (old != NULL && dp->repl == NULL)) {
cerror("Redefining defined variable \"%s\"", dp->name);
}
if (old != NULL) /* We don't need the */
free(old); /* old definition now. */
}
return;
bad_define:
cerror("#define syntax error", NULLST);
inmacro = FALSE; /* Stop <newline> hack */
}
checkparm(c, dp)
register int c;
DEFBUF *dp;
/*
* Replace this param if it's defined. Note that the macro name is a
* possible replacement token. We stuff DEF_MAGIC in front of the token
* which is treated as a LETTER by the token scanner and eaten by
* the output routine. This prevents the macro expander from
* looping if someone writes "#define foo foo".
*/
{
if ((c = isformal(c)) == 0)
mtokensave(dp);
}
FILE_LOCAL int
isformal(c)
register int c;
/*
* Scan the token starting with c. If it is a formal parameter, save
* the MAC_PARM and formal offset, returning TRUE. Else, return FALSE.
*/
{
register int i;
scanid(c); /* Get parm to token[] */
for (i = 0; i < nargs; i++) { /* For each argument */
if (streq(parlist[i], token)) { /* If it's known */
save(MAC_PARM); /* Save the signal */
save(i + 1); /* Save the formal */
return (TRUE); /* Return "gotcha" */
}
}
return (FALSE); /* Not a formal param */
}
FILE_LOCAL
mtokensave(dp)
DEFBUF *dp;
/*
* Save the token in the macro buffer. A magic cookie is saved
* if the token is identical to the macro name, so the expansion
* doesn't recurse.
*/
{
register char *cp;
if (dp != NULL && streq(dp->name, token)) /* Macro name in body */
save(DEF_MAGIC); /* Save magic marker */
for (cp = token; *cp != EOS;) /* And save */
save(*cp++); /* The token itself */
}
#if STRING_FORMAL
stparmscan(delim, dp)
int delim;
register DEFBUF *dp;
/*
* Scan the string (starting with the given delimiter).
* The token is replaced if it is the only text in this string or
* character constant. The algorithm follows checkparm() above.
* Note that scanstring() has approved of the string.
*/
{
register int c;
/*
* Warning -- this code hasn't been tested for a while.
* It exists only to preserve compatibility with earlier
* implementations of cpp. It is not part of the Draft
* ANSI Standard C language.
*/
save(delim);
instring = TRUE;
while ((c = get()) != delim
&& c != '\n'
&& c != EOF_CHAR) {
if (type[c] == LET) /* Maybe formal parm */
checkparm(c, (DEFBUF *) NULL); /* But no DEF_MAGIC */
else {
save(c);
if (c == '\\')
save(get());
}
}
instring = FALSE;
if (c != delim)
cerror("Unterminated string in macro body", NULLST);
save(c);
}
#else
stparmscan(delim)
int delim;
/*
* Normal string parameter scan.
*/
{
register char *wp;
register int i;
extern int save();
wp = workp; /* Here's where it starts */
if (!scanstring(delim, save))
return; /* Exit on scanstring error */
#if 0 && STRING_FORMAL
/*
* This code -- if reenabled -- recognizes a formal parameter
* if it is the only component of a string:
* #define foo(bar, v) printf("%" "bar" "\n", v);
* This has been superceded by # stringizing.
*/
workp[-1] = EOS; /* Erase trailing quote */
wp++; /* -> first string content byte */
#if (NMACPARS * 2) + 1 > 255
<< error, the following won't work >>
#endif
for (i = 0; i < nargs; i++) {
if (streq(parlist[i], wp)) {
*wp++ = MAC_PARM; /* Parameter signal */
*wp++ = i + NMACPARS + 1; /* Out of range marker */
*wp = wp[-3]; /* Add on closing quote */
workp = wp + 1; /* Reset string end */
return;
}
}
workp[-1] = wp[-1]; /* Nope, reset end quote. */
#endif
}
#endif
doundef()
/*
* Remove the symbol from the defined list.
* Called from the #control processor.
*/
{
register int c;
if (type[(c = skipws())] != LET)
cerror("Illegal #undef argument", NULLST);
else {
scanid(c); /* Get name to token[] */
if (defendel(token, TRUE) == NULL) {
cwarn("Symbol \"%s\" not defined in #undef", token);
}
}
}
textput(text)
char *text;
/*
* Put the string in the parm[] buffer.
*/
{
register int size;
size = strlen(text) + 1;
if ((parmp + size) >= &parm[NPARMWORK])
cfatal("Macro work area overflow", NULLST);
else {
strcpy(parmp, text);
parmp += size;
}
}
charput(c)
register int c;
/*
* Put the byte in the parm[] buffer.
*/
{
if (parmp >= &parm[NPARMWORK])
cfatal("Macro work area overflow", NULLST);
else {
*parmp++ = c;
}
}
/*
* M a c r o E x p a n s i o n
*/
static DEFBUF *macro; /* Catches start of infinite macro */
expand(tokenp)
register DEFBUF *tokenp;
/*
* Expand a macro. Called from the cpp mainline routine (via subroutine
* macroid()) when a token is found in the symbol table. It calls
* expcollect() to parse actual parameters, checking for the correct number.
* It then creates a "file" containing a single line containing the
* macro with actual parameters inserted appropriately. This is
* "pushed back" onto the input stream. (When the get() routine runs
* off the end of the macro line, it will dismiss the macro itself.)
*/
{
register int c;
register FILEINFO *file;
extern FILEINFO *getfile();
#if DEBUG
if (debug) {
dumpadef("expand entry", tokenp);
dumpunget("expand entry");
}
#endif
/*
* If no macro is pending, save the name of this macro
* for an eventual error message.
*/
if (recursion++ == 0)
macro = tokenp;
else if (recursion == RECURSION_LIMIT) {
cerror("Recursive macro definition of \"%s\"", tokenp->name);
fprintf(stderr, "(Defined by \"%s\")\n", macro->name);
if (rec_recover) {
do {
c = get();
} while (infile != NULL && infile->fp == NULL);
unget();
recursion = 0;
return;
}
}
/*
* Here's a macro to expand.
*/
nargs = 0; /* Formals counter */
parmp = parm; /* Setup parm buffer */
switch (tokenp->nargs) {
case (-2): /* __LINE__ */
sprintf(work, "%d", line);
ungetstring(work);
break;
case (-3): /* __FILE__ */
for (file = infile; file != NULL; file = file->parent) {
if (file->fp != NULL) {
sprintf(work, "\"%s\"", (file->progname != NULL)
? file->progname : file->filename);
ungetstring(work);
break;
}
}
break;
default:
/*
* Nothing funny about this macro.
*/
if (tokenp->nargs < 0)
cfatal("Bug: Illegal __ macro \"%s\"", tokenp->name);
while ((c = skipws()) == '\n') /* Look for (, skipping */
wrongline = TRUE; /* spaces and newlines */
if (c != '(') {
/*
* If the programmer writes
* #define foo() ...
* ...
* foo [no ()]
* just write foo to the output stream.
*/
unget();
cwarn("Macro \"%s\" needs arguments", tokenp->name);
fputs(tokenp->name, stdout);
return;
}
else if (expcollect()) { /* Collect arguments */
if (tokenp->nargs != nargs) { /* Should be an error? */
cwarn("Wrong number of macro arguments for \"%s\"",
tokenp->name);
}
#if DEBUG
if (debug)
dumpparm("expand");
#endif
} /* Collect arguments */
case DEF_NOARGS: /* No parameters just stuffs */
expstuff(tokenp); /* Do actual parameters */
} /* nargs switch */
#if DEBUG
if (debug)
dumpunget("expand exit");
#endif
}
FILE_LOCAL int
expcollect()
/*
* Collect the actual parameters for this macro. TRUE if ok.
*/
{
register int c;
register int paren; /* For embedded ()'s */
extern int charput();
for (;;) {
paren = 0; /* Collect next arg. */
while ((c = skipws()) == '\n') /* Skip over whitespace */
wrongline = TRUE; /* and newlines. */
if (c == ')') { /* At end of all args? */
/*
* Note that there is a guard byte in parm[]
* so we don't have to check for overflow here.
*/
*parmp = EOS; /* Make sure terminated */
break; /* Exit collection loop */
}
else if (nargs >= NMACPARS) /* Should be an error */
cfatal("Too many actual parameters in macro expansion", NULLST); parlist[nargs++] = parmp; /* At start of new arg */
for (;; c = cget()) { /* Collect arg's bytes */
if (c == EOF_CHAR) {
cerror("end of file within macro argument", NULLST);
return (FALSE); /* Sorry. */
}
else if (c == '\\') { /* Quote next character */
charput(c); /* Save the \ for later */
charput(cget()); /* Save the next char. */
continue; /* And go get another */
}
else if (type[c] == QUO) { /* Start of string? */
scanstring(c, charput); /* Scan it off */
continue; /* Go get next char */
}
else if (c == '(') /* Worry about balance */
paren++; /* To know about commas */
else if (c == ')') { /* Other side too */
if (paren == 0) { /* At the end? */
unget(); /* Look at it later */
break; /* Exit arg getter. */
}
paren--; /* More to come. */
}
else if (c == ',' && paren == 0) /* Comma delimits args */
break;
else if (c == '\n') /* Newline inside arg? */
wrongline = TRUE; /* We'll need a #line */
charput(c); /* Store this one */
} /* Collect an argument */
charput(EOS); /* Terminate argument */
#if DEBUG
if (debug)
printf("parm[%d] = \"%s\"\n", nargs, parlist[nargs - 1]);
#endif
} /* Collect all args. */
return (TRUE); /* Normal return */
}
FILE_LOCAL
expstuff(tokenp)
DEFBUF *tokenp; /* Current macro being expanded */
/*
* Stuff the macro body, replacing formal parameters by actual parameters.
*/
{
register int c; /* Current character */
register char *inp; /* -> repl string */
register char *defp; /* -> macro output buff */
int size; /* Actual parm. size */
char *defend; /* -> output buff end */
#if 0 && STRING_FORMAL
int string_magic; /* String formal hack */
#endif
FILEINFO *file; /* Funny #include */
extern FILEINFO *getfile();
file = getfile(NBUFF, tokenp->name);
inp = tokenp->repl; /* -> macro replacement */
defp = file->buffer; /* -> output buffer */
defend = defp + (NBUFF - 1); /* Note its end */
if (inp != NULL) {
while ((c = (*inp++ & 0xFF)) != EOS) {
if (c == MAC_PARM) {
c = (*inp++ & 0xFF) - 1; /* Parm number */
#if 0 && STRING_FORMAL
string_magic = ((c >= NMACPARS) != 0);
if (string_magic)
c -= NMACPARS;
#endif
/*
* Replace formal parameter by actual parameter string.
*/
if (c < nargs) {
size = strlen(parlist[c]);
if ((defp + size) >= defend)
goto nospace;
#if 0 && STRING_FORMAL
/*
* Erase the extra set of quotes.
*/
if (string_magic && defp[-1] == parlist[c][0]) {
strcpy(defp-1, parlist[c]);
defp += (size - 2);
}
else {
strcpy(defp, parlist[c]);
defp += size;
}
#else
strcpy(defp, parlist[c]);
defp += size;
#endif
}
}
else if (defp >= defend) {
nospace: cfatal("Out of space in macro \"%s\" arg expansion",
tokenp->name);
}
else {
*defp++ = c;
}
}
}
*defp = EOS;
#if DEBUG
if (debug > 1)
printf("macroline: \"%s\"\n", file->buffer);
#endif
}
#if DEBUG
dumpparm(why)
char *why;
/*
* Dump parameter list.
*/
{
register int i;
printf("dump of %d parameters (%d bytes total) %s\n",
nargs, parmp - parm, why);
for (i = 0; i < nargs; i++) {
printf("parm[%d] (%d) = \"%s\"\n",
i + 1, strlen(parlist[i]), parlist[i]);
}
}
#endif